if( typeof JsfTk == "undefined") {

/**
 * Client side 'interface'.  
 */
function JsfTkAjaxContext() {}

/**
 * @param  form the form to submit or an element in the form. 
 * @param  inputIds array of the ids of the input elements to submit (inputs already in the form). 
 *      Implementations may choose to submit all form information anyways, but at a minimum, 
 *      the given inputs will be submitted.
 * @param refreshIds the components to refresh (must be combined with an 
 *      equivalent server side call to guarantee re-rendering)
 * @param validate (optional) if true, it is a *suggestion* that the form should be 
 *    validated before submission. May be ignored by the framework or 
 * 	  if there is no client side validation.
 * @return true if the form was submitted via ajax, false otherwise.
 */
 JsfTkAjaxContext.prototype.ajaxSubmit = 
	function(form, inputIds, refreshIds, validate) { return false; }

/**
 * Declare class/namespace JsfToolkitFunctions.
 */
function JsfToolkitFunctions() { 

this.cloneDefined = function(object) {
	var copy = {};
	
}; // end cloneDefined

/**
 * Combines the user defined properties of all the arguments together
 * into a new object, which is returned. undefined properties (i.e. properties
 * with a value of undefined) are skipped.
 */
this.shallowCopy = function() {
   var i;
   var srcObj;
   var destObj = {};
   if(arguments.length > 0 ) {
      for( i = 0; i < arguments.length; i++ ) {
         srcObj = arguments[i];
         if( srcObj ) { // make sure it's not null
            for( var prop in srcObj ) {
            	// don't copy undefined properties
            	if( typeof srcObj[prop] !== 'undefined' ) { 
               		destObj[prop] = srcObj[prop];
				}
            }
         }
      }
   }
   return destObj;
};

this.indexOf = function(array,thing,eqFun) {
	eqFun = eqFun || function(a,b) { return a == b; }
  	for( i = 0; i < array.length; i++ ) {
  		if( eqFun(array[i], thing) ) {
  			return i;
  		}
  	}	
  	return -1;
}

/**
 * Moves all of src's children in to dest.
 */
this.moveChildren = function(src,dest) {   
   while(src.firstChild) {
      dest.appendChild(src.removeChild(src.firstChild));
   }
   return dest;
};

/**
 * Copies all src's attributes to dest.
 */
this.copyAttributes = function(src,dest) {
   var name;
   var value;
   for(var i = 0; i < src.attributes.length; i++ ) {
      name = src.attributes[i].name;
      value = src.attributes[i].value;
      dest.attributes[name] = value;
   }
   return dest;
};

/**
 * Moves all src's children to dest and copies it's attributes.
 * If dest is not specified, a div element will be created.
 */
this.transfer = function(src,dest) {
   dest = dest || document.createElement("div");      
   this.moveChildren(src,dest);
   return this.copyAttributes(src,dest);
};

/**
 * Finds the first element with the given id, in a search rooted at parent.
 * The whole document is searched if parent is null/undefined.
 * If the id is invalid (doesn't match an element) undefined/null is returned.
 */
 /* public static */
this.byId = function (id, parent){
        var c;
        var recurse;
        if (id && typeof id == 'string') {
            if( parent ) {
              for( c in parent.childNodes ) {
                recurse = null;
                    if(c.id == id) {
                       return c;
                    } else {
                       recurse = byId(id,c);
                       if(recurse) {
                          return recurse;
                       }
                    }
              }
              return undefined;
            }
         return document.getElementById(id);
      }
      return id;
    };
    
  /* public static */
this.byIds = function (){
      if (arguments.length === 1) {     
         return arguments[0];
      }
      var array = [];
      for (var i = 0; i < arguments.length; i++) {
         array.push(byId(arguments[i]));
      }
      return array;
   };

/**
 * Returns the id of the element. It it is a string
 * it is returned verbatim.
 */
  /* public static */
this.toId = function (elem) {
      if( elem ) {      
           if (typeof elem == 'string') {
            return elem;
           }      
           return elem.id;
        }
        return null;
   };
   
/**
 * Returns an array of the ids of the elements passed in. 
 * Any string elements are returned unaltered (since they 
 * are probably ids)
 */   
  /* public static */
this.toIds = function () {
      var array = [];
      if(arguments.length === 1 && arguments[0] && arguments[0].constructor == Array) {
         args = arguments[0];
      }  else {
         args = arguments;
      }        
      for( var i = 0; i < args.length; i++ ) {
         if( args[i] ) {
            array.push( this.toId(args[i]) );
         }
      }
      return array;
   };

/** 
 * Finds all the child elements with the given tag name. If
 * parent is not specified, searches the whole document.
 */
  /* public static */
this.byTag = function (tag, parent){
         if(parent) {           
           return JsfTk.byId(parent).getElementsByTagName(tag);
         }
         return document.getElementsByTagName(tag);
    };
    
/**
 * Concatenates the the return of valueFunction for each item, with token as 
 * the delimeter. e.g. 
 * function Foo(foo) {
 *   this.foo = foo;
 * }
 *
 * join([new Foo('bar'),new Foo('baz')],';',function(f) { return f.foo; }) == 'bar;baz'
 *
 * Additional, items that do no have the given property are ignored:
 *
 * function Bar(bar) {
 *   this.bar = bar;
 * } 
 * 
 * join([new Foo('bar'),new Foo('baz'),new Bar('foo')],';', function(f) { return f.foo; }) == 'bar;baz'
 *
 * @param items the sequence to join
 * @param token the delimeter
 * @param valueFunction the function that determines the value to join OR 
 *    a String naming the property to return. e.g. above 'foo'
 * @return see above
 */
/* public static */
this.join = function(items,token,valueFunction) {
	var property;
	if(typeof valueFunction == "String") {
		property = valueFunction;		
		valueFunction = function(f) {
			f[property];
		};
	}

   if(items.length < 1) {
       return "";
   }
   
   var s = "";
   var p;
   for(var i = 0; i < items.length; i++ ) {
       p = valueFunction( items[i] );
       if(p) {
        s += p;
       }
       if(s.length > 0 && items[i + 1] && valueFunction( items[i+1] )) {
         s += token;
       }
   }
   
   return s;
};

/**
 * Return true if foo is null, the empty string,
 * an empty array, or an array of length 1 containing the empty
 * string or null.
 */
this.isEmpty = function(foo) {
      return foo === null || foo === "" || foo === {} || 
         (typeof foo == "array" && foo.length === 1 && 
            (foo === null || foo[0] === ""));
   };

   
this.getForm = function(node) {
	if(node) {
		if( node.nodeName.toUpperCase() === "FORM" ) {
			return node;
		} else {
			return this.getForm(node.parentNode);
		}
	}
}

function appendHidden(form,name,value) {
     var input = document.createElement("input");
   	 input.type = 'hidden';
   	 input.name = input.id = name;
   	 input.value = value;
   	 form.appendChild(input);
   	 return input;
}

} // end function JsfToo...

JsfTk = new JsfToolkitFunctions();


JsfTk.HtmlScriptsId = 'Not Set';
JsfTk.HtmlScriptsResources = '';

// define the default AjaxContext
function DefaultJsfTkAjaxContext() {
	this.prototype = JsfTkAjaxContext.prototype;
	
	function safeReplace(id,markup) {
		var d, temp, last, parent;
		// remove the scripts and whitespace      
		var str = markup.stripScripts().strip();
		if( str.strip().length !== 0 ) {
			if (-1 === id.indexOf(DynaFaces.gViewRoot)) {
				d = DynaFaces.$(id);
				if(!d) {
					alert('Could not find '+id);
				}
				parent = d.parentNode;
				temp = document.createElement("div");
				temp.id = d.id;
				temp.innerHTML = str;
           
				// replace the element with all of temp's children 
				// (might be extra text nodes or something)
				last = temp.lastChild;
				parent.replaceChild(temp.lastChild, d);
				for(i = 0; i < temp.childNodes.length - 1; i++) {
					parent.insertBefore( temp.childNodes[i], last );
				}
			} else {         
				DynaFaces.replace(id,str);
			}      
		}
	}
	
	this.ajaxSubmit = function( form, inputIds, refreshIds, replaceCallback, validate) {
		form = JsfTk.getForm(JsfTk.byId(form));
		var input;
		if( typeof DynaFaces != "undefined" ) {
			replaceCallback = replaceCallback || safeReplace;
			// make sure we have ids (instead of elements)
			inputIds = JsfTk.toIds(inputIds);
			refreshIds = JsfTk.toIds(refreshIds);
			// need to refresh and submit the action component
			refreshIds = refreshIds.concat( JsfTk.HtmlScriptsId );
			DynaFaces.fireAjaxTransaction( form, { 
				render: refreshIds.join(','),
				inputs: inputIds.join(','),
				// add the already rendered resources as a request parameter
				postBody: JsfTk.HtmlScriptsId + '=' + JsfTk.HtmlScriptsResources,
				replaceElement: replaceCallback
				} );
			return true;
		} else if( typeof TrPage != "undefined" ) {
			// for trinidad, we just submit the whole form
			TrPage.getInstance().sendFormPost(form,{});
		} else if( typeof _submitPartialChange != "undefined" ) {
			_submitPartialChange( form, validate );
			return true;
		}
		return false;
	};   	
}; // end function DefaultJsfTkAjaxContext

// assign the context
JsfTk.ajaxContext = new DefaultJsfTkAjaxContext();

} // end if( typeof ...